﻿/*
 * HyperStat.NET - The minimalist's static website compiler.
 * Copyright (C) 2012 Benjamin Brent
 * http://www.hyperstat.net/
 * http://hyperstat.codeplex.com/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace HyperStat
{
    partial class HyperServe
    {
        private StaticHtmlGenerator statGen;

        private static System.Threading.AutoResetEvent listenForNextRequest = new System.Threading.AutoResetEvent(false);

        public HyperServe(StaticHtmlGenerator statGen)
        {
            if (!HttpListener.IsSupported)
            {
                throw new HyperStatException("Windows XP SP2 or Server 2003 is required to use the web server option.");
            }

            this.statGen = statGen;

            _httpListener = new HttpListener();
        }

        private HttpListener _httpListener;

        public string Prefix { get; set; }

        /// <summary>
        /// Starts the HyperServe web server.
        /// </summary>
        public void Start()
        {
            if (String.IsNullOrEmpty(Prefix))
                throw new InvalidOperationException("No prefix has been specified");
            
            ConfigureHystProject();
            
            _httpListener.Prefixes.Clear();
            _httpListener.Prefixes.Add(Prefix);
            _httpListener.Start();
            System.Threading.ThreadPool.QueueUserWorkItem(Listen);
        }

        /// <summary>
        /// Stops the HyperServer web server.
        /// </summary>
        internal void Stop()
        {
            _httpListener.Stop();
            IsRunning = false;
        }

        public bool IsRunning { get; private set; }

        private void ListenerCallback(IAsyncResult result)
        {
            HttpListener listener = result.AsyncState as HttpListener;
            HttpListenerContext context = null;

            if (listener == null) 
                return;

            try
            {
                context = listener.EndGetContext(result);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
                return;
            }
            finally
            {
                listenForNextRequest.Set();
            }
            if (context == null)
                return;

            try
            {
                ProcessRequest(context);
            }
            catch (HyperStatException ex)
            {
                Console.WriteLine("------------------------------------------");
                Console.WriteLine("ABORT: " + ex.Message);
                Console.WriteLine("------------------------------------------");
                Program.Debug(ex.StackTrace);

                if (context != null)
                {
                    context.Response.StatusCode = 500;
                    context.Response.ContentType = "text/html";
                    context.Response.ContentEncoding = Encoding.UTF8;
                    byte[] buffer = Encoding.UTF8.GetBytes(GenError("500 - Internal Server Error (see console for details)"));
                    context.Response.ContentLength64 = buffer.Length;
                    context.Response.OutputStream.Write(buffer, 0, buffer.Length);
                    context.Response.OutputStream.Close();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("ABORT: An error has occurred. Run in verbose mode for details.");
                Program.Debug(ex.Message);
                Program.Debug(ex.StackTrace);

                // Added this try-catch to fix bug #r100
                try
                {
                    // Try to send 500 status
                    if (context != null)
                    {
                        context.Response.StatusCode = 500;
                        context.Response.ContentType = "text/html";
                        context.Response.ContentEncoding = Encoding.UTF8;
                        byte[] buffer = Encoding.UTF8.GetBytes(GenError("500 - Internal Server Error"));
                        context.Response.ContentLength64 = buffer.Length;
                        context.Response.OutputStream.Write(buffer, 0, buffer.Length);
                        context.Response.OutputStream.Close();
                    }
                }
                catch (Exception ex2)
                {
                    // There was a problem, log details to console in verbose mode
                    Program.Debug("There was a problem while sending 500 status.\r\nThis error can probably be ignored:");
                    Program.Debug(ex2.Message);
                    Program.Debug(ex2.StackTrace);
                }
            }
        }

        /// <summary>
        /// Begins listening.
        /// </summary>
        private void Listen(object state)
        {
            while (_httpListener.IsListening)
            {
                _httpListener.BeginGetContext(new AsyncCallback(ListenerCallback), _httpListener);
                listenForNextRequest.WaitOne();
            }
        }
    }
}
